--[[
	任务系统
]]

local filename = "tasksystem.lua"

local skynet = require "skynet"
local cluster = require "skynet.cluster"
require "skynet.manager"

local filelog = require "filelog"
local timetool = require "timetool"
local tabletool = require "tabletool"
local redisdao = require "redisdao"
require "pubenum"

local json = require "cjson"
json.encode_sparse_array(true,1,1)

local routerbase = require "routerbase"

local TaskSystem = routerbase:new()

-------------------------------------------

--任务触发函数列表
--任务id = 函数名
TaskSystem.task_trigger_funcs = {
    --101：使用一次互动道具
    --[101] = {"tablechat"},  
    --102：进行一局斗地主游戏   
    --[102] = {"game_balance_diamond","game_balance_coin"},
    --201：累计赢取10000金币
    --[201] = {"game_balance_diamond","open_jackpot_diamond"},   
    --202：累计赢取50000金币  
    --[202] = {"game_balance_diamond","open_jackpot_diamond"},     
}

--任务回调函数列表
TaskSystem.task_call_funcs = { 
    --101：使用一次互动道具
    --[101] = {"use_once_interact_prop"}, 
    --102：进行一局斗地主游戏 
    --[102] = {"play_once_ddz"},
    --201：累计赢取10000金币
    --[201] = {"total_win_xxxx_diamond"},   
    --202：累计赢取50000金币
    --[202] = {"total_win_xxxx_diamond"},  
}

--任务处理函数
TaskSystem.handler = { }

----------------------------------

--使用一次互动道具
function TaskSystem.handler.use_once_interact_prop(rid, taskid, newtask_list, _, msgbody)
    if not msgbody.propid or msgbody.propid == 0 then
        return
    end

    --判断任务状态
    local taskdata = TaskSystem.CMD.gettaskdata(rid) or {}
    local taskitem = taskdata[tostring(taskid)]
    if not taskitem or taskitem.state ~= ETaskState.ONGOING then
        return
    end

    --设置任务为已完成
    taskitem.state = ETaskState.COMPLETED
    taskitem.progress = taskitem.conditions

    --更新数据库
    cluster.call("datanode", ".router", "update_rk", rid, "rs_task", 0, {task_list_json = json.encode(taskdata)})

    --通知客户端
    local change_tasklist = {taskitem}
    TaskSystem.notify_client(rid,"changetaskstateN",{change_tasklist_json = json.encode(change_tasklist)})
end

--玩一局xxx游戏
function TaskSystem.handler.play_once_xxx(gamename, rid, taskid, newtask_list, _, _, comment)
    if not comment or not comment.table_uuid then
        return
    end

    local nodename = string.match(comment.table_uuid,"(%a+)nod%a_")
    if nodename ~= gamename then
        return
    end
    
    --判断任务状态
    local taskdata = TaskSystem.CMD.gettaskdata(rid) or {}
    local taskitem = taskdata[tostring(taskid)]
    if not taskitem or taskitem.state ~= ETaskState.ONGOING then
        return
    end

    --设置任务为已完成
    taskitem.state = ETaskState.COMPLETED
    taskitem.progress = taskitem.conditions

    --更新数据库
    cluster.call("datanode", ".router", "update_rk", rid, "rs_task", 0, {task_list_json = json.encode(taskdata)})

    --通知客户端
    local change_tasklist = {taskitem}
    TaskSystem.notify_client(rid,"changetaskstateN",{change_tasklist_json = json.encode(change_tasklist)})
end

--玩一局斗地主
function TaskSystem.handler.play_once_ddz(rid, taskid, newtask_list, _, _, comment)
    return TaskSystem.handler.play_once_xxx("ddz", rid, taskid, newtask_list, _, _, comment)
end

--累计赢取xxxxx金币
function TaskSystem.handler.total_win_xxxx_diamond(rid, taskid, newtask_list, valuestruct, _, _)
    local value = valuestruct.value
    if value <= 0 then
        return
    end

    --判断任务状态
    local taskdata = TaskSystem.CMD.gettaskdata(rid) or {}
    local taskitem = taskdata[tostring(taskid)]
    if not taskitem or taskitem.state ~= ETaskState.ONGOING then
        return
    end

    local change_tasklist = { }
    local progress = tonumber(taskitem.progress)
    local conditions = tonumber(taskitem.conditions)
    if progress + value < conditions then
        taskitem.progress = progress + value
    else
        taskitem.state = ETaskState.COMPLETED
        taskitem.progress = conditions
        --开启以该任务为前置的任务
        for _,item in pairs(taskdata) do
            if tonumber(item.pretask) == taskid then
                item.state = ETaskState.ONGOING
                table.insert(change_tasklist,item)
                newtask_list[item.id] = true
            end
        end
    end
    table.insert(change_tasklist,taskitem)

    --更新数据库
    cluster.call("datanode", ".router", "update_rk", rid, "rs_task", 0, {task_list_json = json.encode(taskdata)})

    --通知客户端
    TaskSystem.notify_client(rid,"changetaskstateN",{change_tasklist_json = json.encode(change_tasklist)})
end

---------------------

--初始化任务配置表
function  TaskSystem.init_taskconfig()
    local taskconfig = require "taskconfig" or {}

    local taskconfig_list = {}
    for tasktype,arr in pairs(taskconfig) do
        for _,item in pairs(arr) do
            table.insert(taskconfig_list,item)
        end
    end
    TaskSystem.taskconfig_list = taskconfig_list
end

--通知客户端
function TaskSystem.notify_client(rid, ...)
    local agent = skynet.call(".mainnode", "lua", "cmd", "get_agent_by_rid", rid)
	if agent then
	    skynet.send(agent, "lua", "cmd", "notify_client", ...)
	end
end

--检测任务有效性
function TaskSystem.check_tasks(tasklist)
    local is_update = false

    if not tasklist then
        return
    end

    --重置超过时限的任务
    local tmlist = os.date("*t")
    local curtime = timetool.get_time()
    local newtime = curtime - (tmlist.hour * 3600 + tmlist.min * 60 + tmlist.sec)
    local taskconf_list = TaskSystem.CMD.gettaskconf() or {}
    for _,taskitem in pairs(taskconf_list) do
        local item = tasklist[tostring(taskitem.id)]
        if not item then
            item = TaskSystem.init_taskitem(tasklist,taskitem)
            is_update = true
        end
        local effecttime = tonumber(item.effecttime) or -1
        if effecttime ~= -1 and effecttime < curtime then
            item.effecttime = newtime + taskitem.effecttime * 86400 - 1
            item.progress = 0
            if item.pretask ~= -1 then
                local pretask = tasklist[tostring(item.pretask)]
                if pretask and pretask.effecttime >= curtime and pretask.state >= ETaskState.COMPLETED then
                    item.state = ETaskState.ONGOING
                else
                    item.state = ETaskState.INACTIVE
                end
            else
                item.state = ETaskState.ONGOING
            end
        end
    end
    
    return tasklist,is_update
end

--初始化任务列表
function TaskSystem.init_tasklist()
    local task_list = {}

    local tmlist = os.date("*t")
    local curtime = timetool.get_time()
    local newtime = curtime - (tmlist.hour * 3600 + tmlist.min * 60 + tmlist.sec)
    local taskconf_list = TaskSystem.CMD.gettaskconf() or {}
    for _,taskitem in pairs(taskconf_list) do
        local item = {
            id = taskitem.id,
            conditions = taskitem.conditions,
            progress = 0,
            rewards = tabletool.deepcopy(taskitem.rewards),
            pretask = taskitem.pretask,
        }
        if item.pretask ~= -1 then
            item.state = ETaskState.INACTIVE
        else
            item.state = ETaskState.ONGOING
        end
        if taskitem.effecttime ~= -1 then
            item.effecttime = newtime + taskitem.effecttime * 86400 - 1
        else
            item.effecttime = -1
        end
        task_list[tostring(item.id)] = item
    end
    
    return task_list
end

--初始化某一项任务
function TaskSystem.init_taskitem(tasklist, taskitem)
    if not tasklist or not taskitem then
        return
    end

    local tmlist = os.date("*t")
    local curtime = timetool.get_time()
    local newtime = curtime - (tmlist.hour * 3600 + tmlist.min * 60 + tmlist.sec)
    local item = {
        id = taskitem.id,
        conditions = taskitem.conditions,
        progress = 0,
        rewards = tabletool.deepcopy(taskitem.rewards),
        pretask = taskitem.pretask,
    }
    if item.pretask ~= -1 then
        local pretask = tasklist[tostring(item.pretask)]
        if pretask and pretask.effecttime >= curtime and pretask.state >= ETaskState.COMPLETED then
            item.state = ETaskState.ONGOING
        else
            item.state = ETaskState.INACTIVE
        end
    else
        item.state = ETaskState.ONGOING
    end
    if taskitem.effecttime ~= -1 then
        item.effecttime = newtime + taskitem.effecttime * 86400 - 1
    else
        item.effecttime = -1
    end

    local k = tostring(item.id)
    tasklist[k] = item

    return tasklist[k]
end

---------------------------------------

--获取任务数据
--taskitem = {
--    id = 1,           任务id,3位 百位：任务类型 1日常任务 2挑战任务
--    conditions = 2,   完成条件
--    progress = 3,     完成进度
--    rewards = 4,      任务奖励
--    state = 5,        任务状态 -1未激活 1进行中 2已完成
--    effecttime = 6,   任务有效时间，时间戳格式，超过有效时间，重置任务进度
--    pretask = 7,      前置任务，有前置任务，必须先完成前置任务才能激活, -1表示无前置
--}
--
function TaskSystem.CMD.gettaskdata(rid, is_client)
    local task_list = nil
    local is_update1 = false
    local is_update2 = false

    --从数据库获取任务列表
    local status,result = skynet.pcall(cluster.call, "datanode", ".router", "select_rk", rid, "rs_task")
    if status and result and result[1] ~= 0 then
        task_list = json.decode(result[2][rid].task_list_json) or {}
        if result[1] == 2 then  --新纪录需要初始化
            task_list = TaskSystem.init_tasklist()
            is_update1 = true
        end
    else
        task_list = {}
    end

    --检测任务是否过期
    if is_client then
        task_list,is_update2 = TaskSystem.check_tasks(task_list)
    end

    --更新
    if is_update1 or is_update2 then
        cluster.call("datanode", ".router", "update_rk", rid, "rs_task", 0, {task_list_json = json.encode(task_list)})
    end

    if is_client then
        return json.encode(task_list)
    end


    return task_list
end

--更新任务
function TaskSystem.CMD.updatetask(rid, setargs)
    if not rid or not setargs or not setargs.id then
        return false
    end

    local task_list = TaskSystem.CMD.gettaskdata(rid)
    if not task_list then
        return false
    end
    task_list[tostring(setargs.id)] = setargs
    cluster.call("datanode", ".router", "update_rk", rid, "rs_task", 0, {task_list_json = json.encode(task_list)})

    return true
end

--
--获得任务配置
--
--任务配置项
--taskconfitem = {
--  id = 1,		    --任务id
--	conditions = 2,	--完成条件
--	rewards = 3,	--任务奖励
--	effecttime = 4,	--有效时间，单位：天, -1表示无限制
--}
--
function TaskSystem.CMD.gettaskconf(task_id)
    local taskconf_list = TaskSystem.taskconfig_list

     --从redis读取任务配置表
    if not taskconf_list then
        local result, data = redisdao.query_data(".gmmqredis", "get", "task_conf_list")
        if result and data then
            taskconf_list = json.decode(data)
        end
    end
    if not taskconf_list or not task_id then
        return taskconf_list
    end

    --获得对应id任务的配置
    for k,item in pairs(taskconf_list) do
        if item.id == task_id then
            return item
        end
    end
   
    return nil
end

--
--通过任务id获得任务项
--
function TaskSystem.CMD.gettaskbyid(rid, taskid)
    local task_data = TaskSystem.CMD.gettaskdata(rid)
    if not task_data or task_data[tostring(taskid)] == nil then
        return false
    end
    local taskitemdata = tabletool.deepcopy(task_data[tostring(taskid)])

    return taskitemdata
end

--
--检测是否触发任务
--
function TaskSystem.CMD.check_is_trigger_task(rid, action, subaction, ...)
    local opname = action
    if action == "cmd" then
        opname = subaction
    end

    --检测是否有任务触发，触发则调用相应处理函数
    local newtask_list = {} --新开启的任务
    local taskconfig_list = TaskSystem.CMD.gettaskconf()
    for _,taskitem in pairs(taskconfig_list) do
        if not newtask_list[taskitem.id] then   --新开启的任务不检测
            local trigger_funcs = TaskSystem.task_trigger_funcs[taskitem.id] or {}
            for _,trigger_func in pairs(trigger_funcs) do
                if opname == trigger_func then
                    local call_funcs = TaskSystem.task_call_funcs[taskitem.id] or {}
                    for _,func_name in pairs(call_funcs) do
                        local f = TaskSystem.handler[func_name]
                        if f then
                            if action == "cmd" then
                                f(rid, taskitem.id, newtask_list, ...)
                            else
                                f(rid, taskitem.id, newtask_list, subaction, ...)
                            end
                        end
                    end
                end
            end
        end
    end
    
end

skynet.start(function()
	TaskSystem:start()
    TaskSystem.init_taskconfig()
end)
